/* EBPF opcode support.  -*- c -*-

   Copyright (C) 2019 Free Software Foundation, Inc.

   Contributed by Oracle, Inc.

   This file is part of the GNU Binutils and of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

/*
   Each section is delimited with start and end markers.

   <arch>-opc.h additions use: "-- opc.h"
   <arch>-opc.c additions use: "-- opc.c"
   <arch>-asm.c additions use: "-- asm.c"
   <arch>-dis.c additions use: "-- dis.c"
   <arch>-ibd.h additions use: "-- ibd.h".  */

/* -- opc.h */

#undef CGEN_DIS_HASH_SIZE
#define CGEN_DIS_HASH_SIZE 1

#undef CGEN_DIS_HASH
#define CGEN_DIS_HASH(buffer, value) 0

/* Allows reason codes to be output when assembler errors occur.  */
#define CGEN_VERBOSE_ASSEMBLER_ERRORS

#define CGEN_VALIDATE_INSN_SUPPORTED
extern int bpf_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);


/* -- opc.c */

/* -- asm.c */

/* Parse a signed 64-bit immediate.  */

static const char *
parse_imm64 (CGEN_CPU_DESC cd,
             const char **strp,
             int opindex,
             int64_t *valuep)
{
  bfd_vma value;
  enum cgen_parse_operand_result result;
  const char *errmsg;

  errmsg = (* cd->parse_operand_fn)
    (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
     &result, &value);
  if (!errmsg)
    *valuep = value;

  return errmsg;
}

/* Endianness size operands are integer immediates whose values can be
   16, 32 or 64.  */

static const char *
parse_endsize (CGEN_CPU_DESC cd,
               const char **strp,
               int opindex,
               unsigned long *valuep)
{
  const char *errmsg;

  errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
  if (errmsg)
    return errmsg;

  switch (*valuep)
    {
    case 16:
    case 32:
    case 64:
      break;
    default:
      return _("expected 16, 32 or 64 in");
    }

  return NULL;
}

/* Special check to ensure that the right instruction variant is used
   for the given endianness induced by the ISA selected in the CPU.
   See bpf.cpu for a discussion on how eBPF is really two instruction
   sets.  */

int
bpf_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
{
  CGEN_BITSET isas = CGEN_INSN_BITSET_ATTR_VALUE (insn, CGEN_INSN_ISA);

  return cgen_bitset_intersect_p (&isas, cd->isas);
}


/* -- dis.c */

/* We need to customize the disassembler a bit:
   - Use 8 bytes per line by default.
*/

#define CGEN_PRINT_INSN bpf_print_insn

static int
bpf_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
{
  bfd_byte buf[CGEN_MAX_INSN_SIZE];
  int buflen;
  int status;

  info->bytes_per_chunk = 1;
  info->bytes_per_line = 8;

  /* Attempt to read the base part of the insn.  */
  buflen = cd->base_insn_bitsize / 8;
  status = (*info->read_memory_func) (pc, buf, buflen, info);

  /* Try again with the minimum part, if min < base.  */
  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
    {
      buflen = cd->min_insn_bitsize / 8;
      status = (*info->read_memory_func) (pc, buf, buflen, info);
    }

  if (status != 0)
    {
      (*info->memory_error_func) (status, pc, info);
      return -1;
    }

  return print_insn (cd, pc, info, buf, buflen);
}

/* Signed immediates should be printed in hexadecimal.  */

static void
print_immediate (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                 void *dis_info,
                 int64_t value,
                 unsigned int attrs ATTRIBUTE_UNUSED,
                 bfd_vma pc ATTRIBUTE_UNUSED,
                 int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  if (value <= 9)
    (*info->fprintf_func) (info->stream, "%" PRId64, value);
  else
    (*info->fprintf_func) (info->stream, "%#" PRIx64, value);

  /* This is to avoid -Wunused-function for print_normal.  */
  if (0)
    print_normal (cd, dis_info, value, attrs, pc, length);
}

/* Endianness bit sizes should be printed in decimal.  */

static void
print_endsize (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
               void *dis_info,
               unsigned long value,
               unsigned int attrs ATTRIBUTE_UNUSED,
               bfd_vma pc ATTRIBUTE_UNUSED,
               int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;
  (*info->fprintf_func) (info->stream, "%lu", value);
}


/* -- */

